﻿using System;

namespace nfrastructure.Utility.Core.RabbitMq.Providers
{
    public class SnowflakeId
    {
        private static readonly object SLock = new object();
        private readonly object _lock = new object();
        private long _lastTimestamp = -1;
        public const long Twepoch = 1288834974657;
        private const int WorkerIdBits = 5;
        private const int DatacenterIdBits = 5;
        private const int SequenceBits = 12;
        private const long MaxWorkerId = 31;
        private const long MaxDatacenterId = 31;
        private const int WorkerIdShift = 12;
        private const int DatacenterIdShift = 17;
        public const int TimestampLeftShift = 22;
        private const long SequenceMask = 4095;
        private static SnowflakeId _snowflakeId;

        public SnowflakeId(long workerId, long datacenterId, long sequence = 0)
        {
            this.WorkerId = workerId;
            this.DatacenterId = datacenterId;
            this.Sequence = sequence;
            if (workerId > 31L || workerId < 0L)
                throw new ArgumentException(string.Format("worker Id can't be greater than {0} or less than 0", (object)31L));
            if (datacenterId > 31L || datacenterId < 0L)
                throw new ArgumentException(string.Format("datacenter Id can't be greater than {0} or less than 0", (object)31L));
        }

        public long WorkerId { get; protected set; }

        public long DatacenterId { get; protected set; }

        public long Sequence { get; internal set; }

        public static SnowflakeId Default()
        {
            lock (SnowflakeId.SLock)
            {
                if (SnowflakeId._snowflakeId != null)
                    return SnowflakeId._snowflakeId;
                Random random = new Random();
                int result1;
                if (!int.TryParse(Environment.GetEnvironmentVariable("CAP_WORKERID", EnvironmentVariableTarget.Machine), out result1))
                    result1 = random.Next(31);
                int result2;
                if (!int.TryParse(Environment.GetEnvironmentVariable("CAP_DATACENTERID", EnvironmentVariableTarget.Machine), out result2))
                    result2 = random.Next(31);
                return SnowflakeId._snowflakeId = new SnowflakeId((long)result1, (long)result2, 0L);
            }
        }

        public virtual long NextId()
        {
            lock (this._lock)
            {
                long num = this.TimeGen();
                if (num < this._lastTimestamp)
                    throw new Exception(string.Format("InvalidSystemClock: Clock moved backwards, Refusing to generate id for {0} milliseconds", (object)(this._lastTimestamp - num)));
                if (this._lastTimestamp == num)
                {
                    this.Sequence = this.Sequence + 1L & 4095L;
                    if (this.Sequence == 0L)
                        num = this.TilNextMillis(this._lastTimestamp);
                }
                else
                    this.Sequence = 0L;
                this._lastTimestamp = num;
                return num - 1288834974657L << 22 | this.DatacenterId << 17 | this.WorkerId << 12 | this.Sequence;
            }
        }

        protected virtual long TilNextMillis(long lastTimestamp)
        {
            long num = this.TimeGen();
            while (num <= lastTimestamp)
                num = this.TimeGen();
            return num;
        }

        protected virtual long TimeGen()
        {
            return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        }
    }
}
